#!/usr/bin/python
# -*- coding: utf-8 -*-

''' compilerSLA.py

Module permettant de compiler le code sla vers du c.

2009-2010, Elias Capito et Jonathan Neuhaus, HE-Arc

'''

import AST
from AST import addToClass
import string
import re

# Contient les opérations 
opNames = {
	1 : { # Operations unaire
		'-' : '-',
		'+' : '' ,
		'~' : '~',
	},

	2: { # Operations Binaire
		'+' : '+', 
		'-' : '-',
		'*' : '*',
		'/' : '/',
		'>' : '>',
		'<' : '<',
		'>=' : '>=',
		'<=' : '<=',
		'==' : '==',
		'<>' : '!=',
		'!=' : '!=',
		'|' : '|',
		'&' : '&',
		'||' : '||',
		'&&' : '&&',
		'^' : '^',
		'>>' : '>>',
		'<<' : '<<',
	}
}

# Liste contenant les éléments qui ne doivent pas avoir de ; à la fin
nodeWithNoEndline = (
	'for',
	'loop',
	'if',
	'elif',
	'else',
	'select',
	'func',
	'configUSART',
)

# -------- Main --------
# Opération sur le premier noeud programme
@addToClass(AST.Node)
def main(self):
	# Génération des includes
	include  = "/****** AUTOGENERATED CODE ******/\n"
	include += "#include <avr/io.h>\n"
	include += "#include <stdlib.h>\n"
	include += "#include <util/delay.h>\n\n"

	# Génération des defines
	define  = "#define bitGet(var,bit) ((var) & (0x01 << (bit)))\n"
	define += "#define bitSet(var,bit) ((var) |= (0x01 << (bit)))\n"
	define += "#define bitClear(var,bit) ((var) &= ~(0x01 << (bit)))\n"
	define += "#define bitFlip(var,bit) ((var) ^= (0x01 << (bit)))\n"
	define += "#define bitWrite(var,bit,val) (val ? bitSet(var,bit) : bitClear(var,bit))\n\n"
	
	# Génération des fonctions de bases
	func = "void waitms(unsigned int time){\n"
	func += "	int i = 0;\n"
	func += "	for(i=0;i<time;i++) _delay_ms(1); \n}"
	func += "\n"
	
	func += "void waits(unsigned int time){\n"
	func += "	int i = 0;\n"
	func += "	for(i=0;i<time;i++) _delay_ms(1000); \n"
	func += "}\n"
	
	func += "char USARTReadChar(void) { \n"
	func += "	while (!(UCSRA & (1<<RXC))); \n"
	func += "	return UDR;\n"
	func += "}\n"
	
	func += "void USARTSendChar(char msg)\n"
	func += "{\n"
	func += "	while (!( UCSRA & (1<<UDRE))) {};\n"
	func += "	UDR = msg;\n"
	func += "}\n"
	
	func += "void USARTSendString(char *msg)\n"
	func += "{\n"
	func += "	while (*msg != '\\0') {\n"
	func += "		USARTSendChar((char)*msg);\n"
	func += "		msg++;\n"
	func += "	}\n"
	func += "}\n"

	func += "void USARTSendVar(int var)\n"
	func += "{\n"
	func += "	char buffer[6];\n"
	func += "	itoa(var, buffer, 10);\n"
	func += "	USARTSendString(buffer);\n"
	func += "}\n"

	func += "void setPWMT0Val(int value) { \n"
	func += "	OCR0A = value;\n"
	func += "}\n"
	func  += "/***** END AUTOGENERATED CODE *****/\n\n/***** USER FUNCTIONS *****/\n"
	
	main = ""
	
	# Initialisation des variables utiliser dans un noeud programme
	for var in self.vars:
		main += "int %s=0;\n" % (var)
	
	# Parcours des enfants pour pouvoir séparer les fonctions définies du reste du code
	for c in self.children:
		if c.type == "func":
			func += c.compile()
			func += "\n"
		else:
			main += c.compile()
			#Si le type de noeud ne se trouve pas dans la liste on lui ajoute un ;
			if c.type not in nodeWithNoEndline:
				main +=";\n"
			else:
				main +="\n"
	# Ordonne les différentes générations
	result = include
	result += define
	result += func
	result += "/***** END USER FUNCTIONS *****/\n\n/***** MAIN FUNCTION *****/\n"
	result += "int main(void){\n%s" % (main)
	result += "return 0;\n}"
	return result
	
# -------- Programme --------
# Opération par défaut pour un noeud
@addToClass(AST.Node) 
def compile(self):
	result = ""
	for c in self.children: 
		result += c.compile()
		#Si le type de noeud ne se trouve pas dans la liste on lui ajoute un ;
		if c.type not in nodeWithNoEndline:
			result +=";\n"
		else:
			result +="\n"
	return result
	
# Opération sur les noeuds programmes sauf le premier
@addToClass(AST.ProgramNode)
def compile(self):
	result = ''
	# Initialisation des variables utiliser dans un noeud programme
	for var in self.vars:
		result += "int %s=0;\n" % (var)
	
	for c in self.children: 
		result += c.compile()
		#Si le type de noeud ne se trouve pas dans la liste on lui ajoute un ;
		if c.type not in nodeWithNoEndline:
			result +=";\n"
		else:
			result +="\n"
	return result
	
# -------- Général --------
# Opération sur un noeud token
@addToClass(AST.TokenNode)
def compile(self):
	return self.tok

# Opération sur un noeud string
@addToClass(AST.StringNode)
def compile(self):
	return self.string
	
# Opération sur un noeud d'opération
@addToClass(AST.OpNode)
def compile(self):
	if self.nbargs==1:
		result = opNames[self.nbargs][self.op]
		result += self.children[0].compile()
	else:
		result = self.children[0].compile()
		result += opNames[self.nbargs][self.op]
		result += self.children[1].compile()
	return result

# Opération sur un noeud d'assignation
@addToClass(AST.AssignNode)
def compile(self):
	result = "%s=" % self.children[0].compile()
	result += self.children[1].compile()
	return result

# Opération sur un noeud de parenthèse
@addToClass(AST.ParenthesesNode)
def compile(self):
	return "(%s)" % (self.children[0].compile())
	
# -------- Boucle --------
# Opération sur un noeud while
@addToClass(AST.LoopNode)
def compile(self):
	result = "while"
	# Condition
	result += "(%s)" % self.children[0].compile()
	# Contenu
	result += "{\n%s}" % (self.children[1].compile())
	return result

# Opération sur un noeud for
@addToClass(AST.ForNode)
def compile(self):
	result="for"
	# Condition
	result+= "(%s;%s;%s)" % (self.children[0].compile(),self.children[1].compile(),self.children[2].compile())
	# Contenu
	result +="{\n%s}" % (self.children[3].compile())
	return result

# -------- Condition --------
# Opération sur un noeud if
@addToClass(AST.IfNode)
def compile(self):
	result="if"
	# Condition
	result+= "(%s)" % (self.children[0].compile())
	# Contenu
	result +="{\n%s}" % (self.children[1].compile())
	return result

# Opération sur un noeud elif
@addToClass(AST.ElifNode)
def compile(self):
	# Ajoute le le bloc if en premier
	result = self.children[0].compile()
	result += "else if"
	# Condition du else if
	result += "(%s)" % (self.children[1].compile())
	# Contenu du else if
	result += "{\n%s}" % (self.children[2].compile()) 
	return result

# Opération sur un noeud else
@addToClass(AST.ElseNode)
def compile(self):
	# Ajoute bloc if en premier
	result = self.children[0].compile()
	result += "else"
	# Contenu du else
	result += "{\n%s}" % (self.children[1].compile()) 
	return result

# Opération sur un noeud select
@addToClass(AST.SelectNode)
def compile(self):
	result = "switch"
	# Condition
	result += "(%s)" % (self.children[0].compile())
	# Contenu
	result += "{\n"
	# Ajoute tout les cases
	for case in self.children[1:]:
		result += case.compile()
	result += "}"
	return result

# Opération sur un noeud case
@addToClass(AST.CaseNode)
def compile(self):
	result = "case"
	# Valeur case
	result += " %s :\n" % (self.children[0].compile())
	# Contenu
	result += self.children[1].compile()
	return result
	
# Opération sur un noeud break
@addToClass(AST.BreakNode)
def compile(self):
	return "break"

# -------- Fonction --------
# Opération sur un noeud definition de fonction
@addToClass(AST.FuncDefNode)
def compile(self):
	# Contrôle s'il existe un return pour connaître le paramètre de retour
	if self.checkReturn() == "return":
		result = "int"
	else:
		result = "void"
		
	result += " %s" % (self.funcName)
	
	# Contrôle si la fonction contient des paramètres
	if len(self.funcParams) == 0:
		result += "(void)"
	else:
		result += "(%s)" % (",".join(["int "+param for param in self.funcParams]))
		
	# Contenu fonction
	result += "{\n%s}" % (self.children[0].compile())
	return result
	
# Opération sur le noeud return
@addToClass(AST.ReturnNode)
def compile(self):
	return "return %s" %(self.children[0].compile())

# Opération sur un noeud d'utilisation de fonction
@addToClass(AST.FuncUseNode)
def compile(self):
	result = "%s(" % (self.funcName)
	# Ajoute les paramètres
	if len(self.children)<>0:
		result += "%s" % (self.children[0].compile())
	result += ")"
	return result

# Opération sur les paramètres d'utilisation pour une fonction
@addToClass(AST.ParamsUseNode)
def compile(self):
	return  ",".join([param.compile() for param in self.children])
# -------- Opération de bit --------
# Opération sur un noeud enable
@addToClass(AST.EnableNode)
def compile(self):
	# Contrôle si c'est une variable ou un pin
	match = re.match(r'\bPIN[A-Z][0-7]?\b',self.vars[0])
	if match :
		if self.vars[0][-1] in string.digits:
			result = "bitSet(PORT%s, %s)" % (self.vars[0][-2],self.vars[0][-1])
		else:
			result = "PORT%s = 0xff" % (self.vars[0][-1])
	else:
		if len(self.vars) == 1:
			result = "%s = 0xff" % (self.vars[0])
		else: 
			result = "bitSet(%s, %s)" % (self.vars[0],self.vars[1])
	return result

# Opération sur un noeud disable
@addToClass(AST.DisableNode)
def compile(self):
	# Contrôle si c'est une variable ou un pin
	match = re.match(r'\bPIN[A-Z][0-7]?\b',self.vars[0])
	if match :
		if self.vars[0][-1] in string.digits:
			result = "bitClear(PORT%s, %s)" % (self.vars[0][-2],self.vars[0][-1])
		else:
			result = "PORT%s = 0x00" % (self.vars[0][-1])
	else:
		if len(self.vars) == 1:
			result = "%s = 0x00" % (self.vars[0])
		else: 
			result = "bitClear(%s, %s)" % (self.vars[0],self.vars[1])
	return result

# Opération sur un noeud flip
@addToClass(AST.FlipNode)
def compile(self):
	# Contrôle si c'est une variable ou un pin
	match = re.match(r'\bPIN[A-Z][0-7]?\b',self.vars[0])
	if match :
		if self.vars[0][-1] in string.digits:
			result = "bitFlip(PORT%s, %s)" % (self.vars[0][-2],self.vars[0][-1])
		else:
			result = "PORT%s ^= 0xFFFF" % (self.vars[0][-1])
	else:
		if len(self.vars) == 1:
			result = "%s ^= 0xFFFF" % (self.vars[0])
		else: 
			result = "bitFlip(%s, %s)" % (self.vars[0],self.vars[1])
	return result

# Opération sur un noeud read
@addToClass(AST.ReadNode)
def compile(self):
	# Contrôle si c'est une variable ou un pin
	match = re.match(r'\bPIN[A-Z][0-7]?\b',self.vars[0])
	if match :
		if self.vars[0][-1] in string.digits:
			result = "!bitGet(PIN%s, %s)" % (self.vars[0][-2],self.vars[0][-1])
		else:
			result = "~PIN%s" % (self.vars[0][-1])
	else:
		if len(self.vars) == 1:
			result = "%s" % (self.vars[0])
		else: 
			result = "bitGet(%s, %s)" % (self.vars[0],self.vars[1])
	return result

# Opération sur un noeud write
@addToClass(AST.WriteNode)
def compile(self):
	# Selon le contenu on crée le noeud corespondant
	if self.vars[-1].compile() == '0':
		return AST.DisableNode(self.vars[:-1]).compile()
	else:
		return AST.EnableNode(self.vars[:-1]).compile()
		
# -------- ATMEL --------
# Opération sur un noeud de configuration de direction des ports
@addToClass(AST.ConfigDirNode)
def compile(self):
	if self.pin[-1] in string.digits:
		result = "bitWrite(DDR%s, %s, %s)" % (self.pin[-2],self.pin[-1], '0' if self.inout == "in" else '1')
	else:
		result = "DDR%s = %s" % (self.pin[-1], '0x00' if self.inout == "in" else '0xff') 
	return result

# Opération sur un noeud de configuration de pull up des ports configurés en entrés
@addToClass(AST.ConfigPullUpNode)
def compile(self):
	if self.pin[-1] in string.digits:
		result = "bitWrite(PORT%s, %s, %s)" % (self.pin[-2],self.pin[-1], '1' if self.onoff == "on" else '0')
	else:
		result = "PORT%s = %s" % (self.pin[-1], '0xff' if self.onoff == "on" else '0x00') 
	return result

# Opération sur un noeud de configuration de la liason RS232
@addToClass(AST.ConfigUSARTNode)
def compile(self):
	result  = "bitSet(UCSRB, RXEN);\n"
	result += "bitSet(UCSRB, TXEN);\n"

	# Contrôle que les variables contiennent des valeurs attendues
	if self.dataBits not in ['5','6','7','8','9']:
		compilationError("Parametre dataBits invalide (valeur valide: 5-9)")
	if self.parity not in ['0','1','2']:
		compilationError("Parametre parity invalide (valeur valide: 0-2)")
	if self.stopBits not in ['1','2']:
		compilationError("Parametre stopBits invalide (valeur valide: 1-2)")
		
	if self.dataBits=='9':
		result += "bitSet(UCSRB, UCSZ2);\n"
	if self.dataBits in ['6','8']:
		result += "bitSet(UCSRC, UCSZ0);\n"	
	if self.dataBits in ['7','8']:
		result += "bitSet(UCSRC, UCSZ1);\n"
		
	if self.parity in ['1','2']:
		result += "bitSet(UCSRC, UPM1);\n"
	if self.parity == '2':
		result += "bitSet(UCSRC, UPM0);\n"
	if self.stopBits == '2':
		result += "bitSet(UCSRC, USBS);\n"

	result += "UBRRL = (((F_CPU / (%s * 16UL))) - 1);\n" % (self.baudrate)
	result += "UBRRH = (((F_CPU / (%s * 16UL))) - 1) >>8 ;\n" % (self.baudrate) 
	return result

# Opération sur un noeud de configuration de la modulation de la largeur d'impulsion sur le pin B2
@addToClass(AST.ConfigPWMT0Node)
def compile(self):
	if self.prescaler not in ['0','1','2','3','4','5']:
		compilationError("Parametre prescaler invalide (valeur valide: 0-5)")
	result = "TCCR0A |= (1<<WGM00) | (1<<WGM01) | (1<<COM0A1);\n"
	result += "TCCR0B |= %s" % (self.prescaler)
	return result
		
# -------- Divers --------
# Contrôle si un noeud return existe dans les noeuds enfant
@addToClass(AST.Node)
def checkReturn(self):
	for c in self.children:
		if c.type == "return":
			return "return"
		else:
			check = c.checkReturn()
			if check =="return":
				return check

# Fonction permettant d'afficher un message d'erreur
def compilationError(desc):
	print "*** ERREUR : %s ***" % (desc)
	sys.exit()
	
if __name__ == "__main__":
	from attribVarsSLA import attribVarsSLA
	import sys,os
	try:
		prog = file(sys.argv[1]).read()
		result = attribVarsSLA(prog)
		compiled = result.main()
		name =os.path.splitext(sys.argv[1])[0]+".c"
		outfile = open(name, 'w')
		outfile.write(compiled)
		outfile.close()
		print "Le fichier compile a ete genere sous ce nom: ", name
	except IOError:
		print "*** ERREUR : Le module compilerSLA n'a pas trouve le fichier ***"	
		